
package gitbox;

import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;


// This client thread opens the input and the output streams for a particular client,
// ask the client's name, informs all the clients currently connected to the 
// server about the fact that a new client has joined the chat room, 
// and as long as it receive data, echos that data back to all other clients.
// When the client leaves the chat room this thread informs also all the
// clients about that and terminates. 
public class serverup{

    // Declaration section:
    // declare a server socket and a client socket for the server
    // declare an input and an output stream

    static  Socket clientSocket = null;
    static  ServerSocket serverSocket = null;
    static int limit_conn = 100; // This server can accept up to 10 clients' connections
    static int port_number=2222; // For uploading downloading deleting
    static int port_number_metadata = 4444; // For sending metadata
    static clientThread t[] = new clientThread[limit_conn];
    static public ConcurrentHashMap<String, Integer> no_revisi = new ConcurrentHashMap<String, Integer>();
    static public ConcurrentHashMap<String, Integer> no_revisi2 = new ConcurrentHashMap<String, Integer>();
    
    public static void main(String args[]) {
        // The default port
        if (args.length < 1)
        {
            System.out.println("Usage: java server \n"+"Now using port number="+port_number);
        }
        else
        {
            port_number=Integer.valueOf(args[0]).intValue();
        }

        // Initialization section:
        // Try to open a server socket on port port_number (default 2222)
        // Note that we can't choose a port less than 1023 if we are not
        // privileged users (root)

        try {
            serverSocket = new ServerSocket(port_number);
        }
        catch (IOException e)
        {System.out.println(e);}

        // Create a socket object from the ServerSocket to listen and accept
        // connections.
        // Open input and output streams for this socket will be created in
        // client's thread since every client is served by the server in
        // an individual thread

        while(true)
        {
            try
            {
                int i;
                clientSocket = serverSocket.accept();
                for(i=0; i<limit_conn && (t[i] !=null); ++i);
                if (i<limit_conn){
                    (t[i] = new clientThread(clientSocket,t)).start();
                }
            }
            catch (IOException e)
            {
                    System.out.println(e);
            }
        }
    }
} 

class clientThread extends Thread implements Serializable{
    // Thread yang menangani adanya client connect di port 2222 untuk upload, download
    static ObjectInputStream ois = null;
    static ObjectOutputStream oos = null;
    static InputStream is = null;
    static OutputStream os = null;

    Socket clientSocket = null;
    String username = null;
    static Connections conns = null;
    static UserModel usermodel = null;
    static ConcurrentHashMap<String, Metadata> ServerStatus = null; // Structured <username, concurrenthashmap<filename, lastmodified> >
    //static ConcurrentHashMap<String, Metadata> Files = null;
    static File directory;
    static File[] ListFile;
    
    clientThread t[];
    PrintWriter pw = null;

    //static HashMap<String, Object> Message = null;
    static ArrayList<Message> MessageList = null;
    static Message MessageIn = null;
    static Message MessageOut = null;

    static String workdir = null;
    public clientThread(Socket clientSocket, clientThread[] t){
        this.clientSocket = clientSocket;
        this.t = t;
        conns = new Connections();
        usermodel = new UserModel(conns);
        ServerStatus = new ConcurrentHashMap<String, Metadata>();
        ChangeWorkDir("D:\\box");

        //Message = new HashMap<String, Object>();
        MessageList = new  ArrayList<Message>();
        FillMessage();
    }
    public boolean ValidateUser(String usrnm, String pswd){
        return usermodel.matchUsernamePassword(usrnm, pswd);
    }
    public void FillMessage(){
        MessageList.add(new Message("SERVER-Valid","valid"));
        MessageList.add(new Message("SERVER-Invalid","invalid"));
        MessageList.add(new Message("SERVER-Exit","exit"));
        MessageList.add(new Message("SERVER-ServerStatus",ServerStatus));
        MessageList.add(new Message("SERVER-RequestFile", null));
    }
    public void sendMessageToClient(){
        try {
            os = clientSocket.getOutputStream();
            oos = new ObjectOutputStream(os);
            oos.writeObject(MessageOut);
        } catch (IOException ex) {
            Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
    public void SendFile(String sourcepathfile){
        // sendfile
          File myFile = new File (sourcepathfile);
          byte [] mybytearray  = new byte [(int)myFile.length()];
          FileInputStream fis;
          BufferedInputStream bis;
          OutputStream oss;
        try {
            fis = new FileInputStream(myFile);
            bis = new BufferedInputStream(fis);
            bis.read(mybytearray,0,mybytearray.length);
            Message msgfile = new Message(myFile.getName().toString(),mybytearray);
            MessageOut = new Message("SERVER-AcceptFile",msgfile);
            sendMessageToClient();
            System.out.println("Sending...");
            bis.close();
            fis.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
        } catch (java.io.IOException e){
            e.printStackTrace();
        }
    }
    public void ReceiveFile(String destpathfile){
        Integer i, no_rev_sekarang = serverup.no_revisi.get(username), length = destpathfile.length();
        String potongan, potongan2;
        for (i=0; i<length && destpathfile.charAt(i)!='\\'; ++i);
        if (i<length)
        {
            potongan = destpathfile.substring(i, length);
            potongan2 = destpathfile.substring(0,i);
        }
        else
        {
            potongan = destpathfile;
            potongan2 = "";
        }
        File file = new File(workdir + "\\" + no_rev_sekarang + potongan);
        System.out.println("yg mau dibaca : "+workdir + "\\" + no_rev_sekarang + "\\" + potongan);

        byte[] mybytearray = new byte[(int)file.length()], mybytearray2;
        Message msgfile = (Message) MessageIn.getContent();
        mybytearray2 = (byte[]) msgfile.getContent();
        
        Boolean sama = false;
        if (mybytearray.length == mybytearray2.length)
        {
            try {
                FileInputStream fis = new FileInputStream(file);
                BufferedInputStream bis = new BufferedInputStream(fis);
                bis.read(mybytearray,0,mybytearray.length);
                for (i=0; i<mybytearray.length && mybytearray[i]==mybytearray2[i]; ++i);
                sama = (i>=mybytearray.length);
            } catch (FileNotFoundException ex) {
                Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
            } catch (java.io.IOException e){
                e.printStackTrace();
            }
        }
        if (!sama)
        {
            //InputStream is = null;
            FileOutputStream fos = null;

            BufferedOutputStream bos = null;
            try {
                //is = clientSocket.getInputStream();
                boolean success = (new File(workdir + "\\" + potongan2)).mkdir();
                System.out.println("mau mbikin folder : "+workdir + "\\" + potongan2);
                if (success)
                {
                    fos = new FileOutputStream(workdir + "\\" + destpathfile);
                    bos = new BufferedOutputStream(fos);
                    bos.write(mybytearray2);
                    bos.flush();
                    bos.close();
                }
            } catch (IOException ex) {
                Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    @Override
    public void run()
    {
        System.out.println("Ada Client = " + clientSocket);
        try {
            // Listening untuk LOGIN
            is = clientSocket.getInputStream();
            ois = new ObjectInputStream(is);
            HashMap<String, String> tmphash = new HashMap<String, String>();
            tmphash = (HashMap<String, String>) ois.readObject();
            username = tmphash.keySet().iterator().next();
            boolean valid = ValidateUser(username, tmphash.get(tmphash.keySet().iterator().next()));
            if(valid){
                // Login Berhasil, True
                System.out.println("client valid");
                if (serverup.no_revisi.get(username)==null)
                {
                    System.out.println("USERNAME = "+username);
                    serverup.no_revisi.put(username, 0);
                    serverup.no_revisi2.put(username, 0);
                }
                // LISTENING
                MessageOut = new Message("SERVER-Valid",null);
                sendMessageToClient();
                while(true){
                     is = clientSocket.getInputStream();
                     ois = new ObjectInputStream(is);
                     MessageIn = (Message) ois.readObject();
                     System.out.println("message = "+MessageIn+", judulnya = "+MessageIn.getTitle());
                     if(MessageIn.getTitle().equals("CLIENT-Exit")){
                         System.out.println("CLIENT-Exit");
                         break;
                     }else if(MessageIn.getTitle().equals("CLIENT-RequestMetadata")){
                         // Kalo klien butuh metadata
                         ReadListFile();
                         Integer no = serverup.no_revisi2.get(username);
                         serverup.no_revisi2.put(username,no+1);
                         // Kirim serverstatus
                         //MessageOut = new Message("SERVER-ServerStatus",ServerStatus);
                         System.out.println("nomor = "+no);
                         MessageOut = new Message("SERVER-ServerStatus",new Message(""+no,ServerStatus));
                         sendMessageToClient();
                     }else if(MessageIn.getTitle().equals("CLIENT-RequestFile")){
                         //System.out.println("");
                         Integer no = serverup.no_revisi.get(username);
                         SendFile(workdir + "\\" + no + "\\" + MessageIn.getContent());
                     }else if(MessageIn.getTitle().equals("CLIENT-DeleteFile")){
                         String path = MessageIn.getContent().toString(), nama;
                         Integer i, length = path.length(), j =0;
                         Integer no = serverup.no_revisi.get(username);
                         
                         for (i=0; i<length && path.charAt(i)!='?'; ++i)
                             j=j*10+(path.charAt(i))-48;
                         nama = path.substring(i+1, length);
                         
                         File fldelete = new File(workdir + "\\" + j);
                         
                         ServerStatus.put(nama, new Metadata(nama, (long)-1, System.currentTimeMillis()));
                         boolean success = (fldelete).mkdir();
                         System.out.println("mau mbikin folder : "+workdir + "\\" + j + "temp = " +path+", trus "+nama);
                         //if(fldelete.delete()){
                             //System.out.println("SERVER--Mendelete File");
                         //}
                     }else if(MessageIn.getTitle().equals("CLIENT-AcceptFile")){
                         Message msgtemp = (Message) MessageIn.getContent();
                         ReceiveFile(msgtemp.getTitle());
                     }else if(MessageIn.getTitle().equals("CLIENT-Beres"))
                     {
                         //sekarang mberesin deh
                         String path = MessageIn.getContent().toString();
                         Integer i, j, length = path.length(), no_rev = 0, no_rev_sekarang = serverup.no_revisi.get(username);

                         for (i=0; i<length; ++i)
                             no_rev = no_rev*10+(path.charAt(i))-48;

                         File fl1 = new File(workdir + "\\" + no_rev), fl2 = new File(workdir + "\\" + no_rev_sekarang), fl3;
                         ConcurrentHashMap<String, Boolean> tmp_hash_map = new ConcurrentHashMap<String, Boolean>();
                         File[] file1 = fl1.listFiles();
                         File[] file2 = fl2.listFiles();
                         boolean success = (fl1).mkdir();
                         //if (success)
                             //System.out.println("Folder yang mau dibikin : "+workdir + "\\" + no_rev);
                         //else
                             //System.out.println("Folder yang mau dibikin (tapi uda ada) : "+workdir + "\\" + no_rev);
                         if (success) //berarti belum ada foldernya, dan g perlu ada, jadi delete lagi aja
                         {
                             System.out.println("wew....");
                             fl1.delete();
                         }
                         else
                         {
                             if (file1!=null)
                                for (i = 0; i < file1.length; ++i)
                                    tmp_hash_map.put(file1[i].getName().toString(),true);
                             if (file1==null)
                                 System.out.println("mau ndelete lho..........\n\n\n\n\n");
                             for (i=0; i<file2.length; ++i)
                             {
                                 String nama = file2[i].getName().toString();
                                 System.out.println("nama file = "+nama+", ada = "+tmp_hash_map.get(nama) + ", size = " +ServerStatus.get(nama).getSize());
                                 if (tmp_hash_map.get(nama) == null && ServerStatus.get(nama).getSize()>=0) //kalo file ini belum ada di folder revisi
                                 {
                                    byte [] mybytearray  = null;
                                    //InputStream is = null;
                                    FileOutputStream fos = null;
                                    BufferedOutputStream bos = null;
                                    try {
                                        fos = new FileOutputStream(workdir + "\\" + no_rev + "\\" + nama);
                                        fl3 = new File(workdir + "\\" + no_rev_sekarang + "\\" + nama);
                                        System.out.println("file yang akan dibuat : "+workdir + "\\" + no_rev + "\\" + nama+", file yang jadi sumbernya : "+workdir + "\\" + no_rev_sekarang + "\\" + nama);
                                        mybytearray = new byte[(int)fl3.length()];

                                        try {
                                            FileInputStream fis = new FileInputStream(fl3);
                                            BufferedInputStream bis = new BufferedInputStream(fis);
                                            bis.read(mybytearray,0,mybytearray.length);
                                            fos.write(mybytearray);
                                        } catch (FileNotFoundException ex) {
                                            Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
                                        } catch (java.io.IOException e){
                                            e.printStackTrace();
                                        }
                                        fos.close();
                                    } catch (IOException ex) {
                                        Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
                                    }
                                 }
                             }
                             if (no_rev > serverup.no_revisi.get(username))
                                 serverup.no_revisi.put(username, no_rev);
                         }
                     }
                }
            }else{
                // False
                System.out.println("client tidak valid");
                MessageOut = new Message("SERVER-Invalid",null);
                sendMessageToClient();
                while(true){
                     is = clientSocket.getInputStream();
                     ois = new ObjectInputStream(is);
                     MessageIn = (Message) ois.readObject();
                     System.out.println("masuk loop mau keluar");

                     if(MessageIn.getTitle().equals("CLIENT-Exit")){
                         System.out.println(MessageIn.getTitle());
                         break;
                     }
                }
            }
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(clientThread.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(clientThread.class.getName()).log(Level.SEVERE, null, ex);
        }
        disconnectToClient();
    }
    public void disconnectToClient(){
        try {
            ois.close();
            oos.close();
            clientSocket.close();
        } catch (IOException ex) {
            Logger.getLogger(clientThread.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public void ChangeWorkDir(String workdirpath){
        workdir = workdirpath;
        directory = new File(workdirpath);
    }

    public void ReadListFile(){
        File temp = new File(workdir+"\\"+serverup.no_revisi.get(username));
        ListFile = temp.listFiles();
        for (int index = 0; index < ListFile.length; index++)
        {
           //Print out the name of files in the directory
           //System.out.println(ListFile[index].toString());
           Metadata mt = new Metadata(ListFile[index].getName(),ListFile[index].getTotalSpace(),ListFile[index].lastModified());
           ServerStatus.put(ListFile[index].getName().toString(), mt);
        }
    }
}